/*
		* Author:	 Sander
		* Date:		 12-apr-2011
		* Name:		 Vec3
		* Version:	1.0
		*
		* This code is property of Sander blahblahblah copyright blahblah.
*/


package javaraytracer;

//PENDING:add function/property description
public class Vec3 //TODO: zuiniger maken, minder 'new' andere aanpak, statisch ofzo
{
	// statics

	public static final Vec3 EMPTY = new Vec3();
	public static final Vec3 ONE = new Vec3(1, 1, 1);
	public static final Vec3 XU = new Vec3(1, 0, 0);
	public static final Vec3 YU = new Vec3(0, 1, 0);
	public static final Vec3 ZU = new Vec3(0, 0, 1);
	public static final Vec3 XD = new Vec3(-1, 0, 0);
	public static final Vec3 YD = new Vec3(0, -1, 0);
	public static final Vec3 ZD = new Vec3(0, 0, -1);
	public static final Vec3[] LIST = new Vec3[] { XU, YU, ZU, XD, YD, ZD };

	// instance
	public double x, y, z;

	// Constructors
	public Vec3(double tx, double ty, double tz)
	{
		x = tx;
		y = ty;
		z = tz;
	}
	public Vec3( Vec3 that )
	{
		this.x = that.x;
		this.y = that.y;
		this.z = that.z;
	}
	public Vec3()
	{
		this.x = 0;
		this.y = 0;
		this.z = 0;
	}

	// function
	public Vec3 add( Vec3 that )
	{
		return new Vec3( this.x + that.x, this.y + that.y, this.z + that.z );
	}

	public Vec3 sub( Vec3 that )
	{
		return new Vec3( this.x - that.x, this.y - that.y, this.z - that.z );
	}

	public Vec3 mul( double f )
	{
		return new Vec3( x * f, y * f, z * f );
	}

	public Vec3 mul( Vec3 that )
	{
		return new Vec3( this.x * that.x, this.y * that.y, this.z * that.z );
	}

	public Vec3 div( double f )
	{
		return new Vec3( x / f, y / f, z / f );
	}

	public Vec3 div( Vec3 that )
	{
		return new Vec3( this.x / that.x, this.y / that.y, this.z / that.z );
	}

	public double dot( Vec3 that )
	{
		return ( (this.x * that.x) + (this.y * that.y) + (this.z * that.z) );
	}

	public Vec3 cross( Vec3 v )
	{
		return new Vec3( ((this.y * v.z) - (v.y * this.z)), ((this.z * v.x) - (this.x * v.z)), ((this.x * v.y) - (this.y * v.x)) );
	}

	public double length()
	{
		return Math.sqrt( length2() );
	}

	public double length2()
	{
		return ( this.dot(this) ); //length squared
	}

	public static Vec3 randomVec3( double a )
	{
		return new Vec3( Math.random() * a, Math.random() * a, Math.random() * a );
	}

	public static Vec3 randomVec3( double a, double b )
	{
		return new Vec3( Math.random() * (b-a) + a, Math.random() * (b-a) + a, Math.random() * (b-a) + a );
	}

	public void normalize()
	{
		double mul = 1.0 / length();
		x *= mul;
		y *= mul;
		z *= mul;
	}

	public Vec3 normalized()
	{
		double mul = (1.0 / length());
		return new Vec3( this.x * mul, this.y * mul, this.z * mul );
	}

	public Vec3 rotate(Vec3 angles)
	{
		return rotateZ(angles.z).rotateX(angles.x).rotateY(angles.y);
	}
	public Vec3 rotate(double xaxis, double yaxis, double zaxis)
	{
		return rotateX(xaxis).rotateY(yaxis).rotateZ(zaxis);
	}

	public Vec3 rotateX(double angle) //rotation around x axis
	{
		return new Vec3(this.x,
						this.y * Math.cos(angle) - this.z * Math.sin(angle),
						this.y * Math.sin(angle) + this.z * Math.cos(angle));
	}

	public Vec3 rotateY(double angle) //rotate around y axis
	{
		return new Vec3(this.x * Math.cos(angle) + this.z * Math.sin(angle),
						this.y,
						-this.x * Math.sin(angle) + this.z * Math.cos(angle));
	}

	public Vec3 rotateZ(double angle) //rotate around z axis
	{
		return new Vec3(this.x * Math.cos(angle) - this.y * Math.sin(angle),
						this.x * Math.sin(angle) + this.y * Math.cos(angle),
						this.z);
	}
	
	public double project(Vec3 on)
	{
		return this.dot(on.normalized());
	}
	
	public Vec3 projectVector(Vec3 on)
	{
		return on.normalized().mul(project(on));
	}
	
	public boolean equals(Vec3 obj)
	{
		if(obj.x == this.x && obj.y == this.y && obj.z == this.z)
		{
			return true;
		}
		return false;
	}
	
	public double compare(Vec3 obj)
	{
		double c = this.sub(obj).length();
		return c;
	}

	@Override
	public Vec3 clone()
	{
		return new Vec3(this);
	}

	@Override
	public String toString()
	{
		return "Vec3 {X:" + this.x + " |Y: " + this.y + " |Z: " + this.z + "}";
	}
}
